﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;

namespace Postnuclear.Dependency.Tree
  {
  /// <summary>
  /// The element of dependencies tree.It holds the information about the dependency from one element
  /// and also the dependencies for the elements which are children of this one.
  /// </summary>
  /// <typeparam name="THolder">The type of the holder.</typeparam>
  /// <typeparam name="TValue">The type of the holders value.</typeparam>
  internal sealed class DependencyTreeElement<THolder, TValue>
    : NotifyValueChanged, IDependencyTreeElement<THolder>
    where THolder : class
    where TValue : class
    {
    #region Public

    // Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="DependencyTreeElement{THolder, TValue}"/> class.
    /// </summary>
    /// <param name="element">The dependency element which is parent for the children elements.</param>
    /// <param name="children">The children.</param>
    public DependencyTreeElement(IDependencyElement<THolder, TValue> element, params IDependencyTreeElement<TValue>[] children)
      {
      Contract.Requires<ArgumentNullException>(element != null);
      Contract.Requires<ArgumentNullException>(children != null);
      Contract.Requires<ArgumentException>(children.Length != 0);

      Element = element;
      Children = children;
      }

    // Methods

    /// <summary>
    /// Updates the dependency holder instance.
    /// </summary>
    /// <param name="holder">The holder.</param>
    public void UpdateHolderValue(THolder holder)
      {
      Element.UpdateHolderValue(holder);

      if (holder != null)
        {
        Children.ForEach(child => child.UpdateHolderValue(Element.Value));
        }
      }

    #endregion

    #region Protected

    // Methods

    /// <summary>
    /// Called when when activation state is changed.
    /// </summary>
    protected override void OnIsActiveChanged()
      {
      if (IsActive)
        {
        Element.ValueChanged += OnElementValueChanged;
        Element.ValueChanged += RaiseValueChanged;
        Children.ForEach(child => child.ValueChanged += RaiseValueChanged);
        }
      else
        {
        Element.ValueChanged -= OnElementValueChanged;
        Element.ValueChanged -= RaiseValueChanged;
        Children.ForEach(child => child.ValueChanged -= RaiseValueChanged);
        }
      }

    #endregion

    #region Private

    // Methods

    [DebuggerHidden]
    private void OnElementValueChanged(object sender, EventArgs e)
      {
      Children.ForEach(child => child.UpdateHolderValue(Element.Value));
      }

    // Fields

    private readonly IDependencyElement<THolder, TValue> Element;
    private readonly IEnumerable<IDependencyTreeElement<TValue>> Children;

    #endregion
    }
  }